// Copyright 2011 Hunter Freyer
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package languish.base;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Terms {

  public static final Term TRUE = abs(abs(ref(2)));
  public static final Term FALSE = abs(abs(ref(1)));
  public static final Term NOT =
    abs(abs(abs(app(app(ref(3), ref(1)), ref(2)))));

  public static final Term CONS =
    abs(abs(abs(app(app(ref(1), ref(3)), ref(2)))));

  public static final Term CAR = abs(app(ref(1), TRUE));
  public static final Term CDR = abs(app(ref(1), FALSE));

  public static Term abs(Term exp) {
    return Term.of(Operation.ABS, exp);
  }

  public static Term app(Term func, Term arg) {
    return Term.of(Operation.APP, func, arg);
  }

  public static Term primitive(Primitive prim) {
    if (prim == null) {
      return nullTerm();
    } else {
      return Term.of(Operation.PRIMITIVE, prim);
    }
  }

  public static Term ref(int i) {
    return Term.of(Operation.REF, i);
  }

  public static Term nativeApplyList(int funcCode, List<Term> args) {
    List<Object> newArgs = new ArrayList<Object>();
    newArgs.add(funcCode);
    newArgs.addAll(args);

    return new Term(Operation.NATIVE_APPLY, newArgs);
  }

  public static Term nativeApply(int funcCode, Term... args) {
    return nativeApplyList(funcCode, Arrays.asList(args));
  }

  public static Term nullTerm() {
    return Term.of(Operation.NULL);
  }

  public static Term ifTerm(Term arg) {
    return Term.of(Operation.IF, arg);
  }

  public static boolean isReduced(Term term) {
    Operation op = term.getOperation();
    return op == Operation.PRIMITIVE ||
      op == Operation.NULL;
  }

  public static boolean isReducible(Term term) {
    Operation op = term.getOperation();
    return op == Operation.APP ||
      op == Operation.IF ||
      op == Operation.NATIVE_APPLY;
  }

  public static Primitive termToPrimitive(Term term) {
    if (term.getOperation() == Operation.NULL) {
      return null;
    }

    if (term.getOperation() == Operation.PRIMITIVE) {
      return (Primitive) term.getFirst();
    }

    // TODO(hjfreyer): get some exceptions up in this bitch.
    throw new ClassCastException("Not reduced, son");
  }

  private Terms() {
  }
}
